#include <iostream>
#include <openssl/rsa.h>
#include <openssl/err.h>
#include <memory>
#include <exception>
using namespace std;

void RSA_DELETER(RSA *q)
{
    cout << "delete RSA" << endl;
    RSA_free(q);
}
void BN_DELETER(BIGNUM *q)
{
    cout << "delete BN" << endl;
    BN_free(q);
}

shared_ptr<RSA> CreateRSAKeyPair()
{
    try
    {
        std::shared_ptr<RSA> r(RSA_new(), RSA_DELETER);
        std::shared_ptr<BIGNUM> b(BN_new(), BN_DELETER);
        //使用提供的公钥指数(并不是公钥，公钥后面计算)
        BN_set_word(b.get(), RSA_F4);
        RSA_generate_key_ex(r.get(), 512, b.get(), nullptr);
        const BIGNUM *np = RSA_get0_n(r.get());
        const BIGNUM *nd = RSA_get0_d(r.get());
        const BIGNUM *ne = RSA_get0_e(r.get());
        std::unique_ptr<unsigned char[]> printnp(new unsigned char[1024]);
        std::unique_ptr<unsigned char[]> printnd(new unsigned char[1024]);
        std::unique_ptr<unsigned char[]> printne(new unsigned char[1024]);
        BN_bn2bin(np, printnp.get());
        BN_bn2bin(nd, printnd.get());
        BN_bn2bin(ne, printne.get());
        cout << printnp.get() << endl;
        cout << printnd.get() << endl;
        cout << printne.get() << endl;
        return r;
    }
    catch (exception &e)
    {
        cout << e.what() << endl;
        return nullptr;
    }
}

int main()
{
    auto r = CreateRSAKeyPair();
    if (r == nullptr)
    {
        cout << "NULL" << endl;
        return 1;
    }
    cout << "Size: " << RSA_size(r.get()) << endl;
    unique_ptr<unsigned char[]> buffer(new unsigned char[32]);
    for (int i = 0; i < 32; ++i)
    {
        *(buffer.get() + i) = 'a' + (i % 26);
    }
    //加密+padding后的数据长度和密钥长度一致，太少不行。每次加密的数据至少比密钥长度少11字节
    unique_ptr<unsigned char[]> outbuffer(new unsigned char[64]);
    int re = RSA_public_encrypt(32, buffer.get(), outbuffer.get(), r.get(), RSA_PKCS1_PADDING);
    if (re < 0)
    {
        unsigned long ulErr = ERR_get_error();
        const char *p = ERR_reason_error_string(ulErr);
        cout << "ERROR: " << p << endl;
    }

    unique_ptr<unsigned char[]> pdeout(new unsigned char[64]);
    for (int i = 0; i < 64 ; ++i)
    {
        *(pdeout.get() + i) = '\0';
    }
    int re1 = RSA_private_decrypt(64, outbuffer.get(), pdeout.get(), r.get(), RSA_PKCS1_PADDING);
    if (re1 < 0)
    {
        unsigned long ulErr = ERR_get_error();
        const char *p = ERR_reason_error_string(ulErr);
        cout << "ERROR: " << p << endl;
    }
    cout<<pdeout.get()<<endl;
    cout << "Ending" << endl;
}
